﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Threading;
using System.Reflection;
using System.Windows.Reflection;
using System.ComponentModel;

namespace System.Windows.Controls
{
    /// <summary>
    /// Implements a custom <see cref="System.Windows.Controls.ItemsControl"/> which arranges its items based on relevancy on the vertical axis and by
    /// a configurable property on the horizontal axis.
    /// </summary>
    public class BandCloudView : System.Windows.Controls.SelectionItemsControl
    {
        /// <summary>
        /// Initializes a <see cref="BandCloudView"/> instance.
        /// </summary>
        public BandCloudView()
        {
            this.DefaultStyleKey = typeof(BandCloudView);

            this.UseItemAnimation = true;
            this.Bands = 10;
            this.HorizontalSortDirection = SortDirections.Descending;
            this.VerticalSortDirection = SortDirections.Descending;
            this.SortBindings = new SortBindings();
            this.VerticalAxisTopLabel = SystemResources.MORE_RELEVANT;
            this.VerticalAxisBottomLabel = SystemResources.LESS_RELEVANT;

            animTimer = new Timer(new TimerCallback(this.OnAnimateItemLoop));
            animTimer.Change(Timeout.Infinite, Timeout.Infinite);
        }

        /// <summary>
        /// Identifies the <see cref="UseItemAnimation"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty UseItemAnimationProperty = RegisterDependencyProperty(typeof(BandCloudView), "UseItemAnimation", typeof(bool));

        /// <summary>
        /// Identifies the <see cref="HorizontalSortField"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty HorizontalSortFieldProperty = RegisterDependencyProperty(typeof(BandCloudView), "HorizontalSortField", typeof(string));

        /// <summary>
        /// Identifies the <see cref="HorizontalSortDirection"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty HorizontalSortDirectionProperty = RegisterDependencyProperty(typeof(BandCloudView), "HorizontalSortDirection", typeof(SortDirections));

        /// <summary>
        /// Identifies the <see cref="VerticalSortField"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty VerticalSortFieldProperty = RegisterDependencyProperty(typeof(BandCloudView), "VerticalSortField", typeof(string));

        /// <summary>
        /// Identifies the <see cref="UseItemAnimation"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty VerticalSortDirectionProperty = RegisterDependencyProperty(typeof(BandCloudView), "VerticalSortDirection", typeof(SortDirections));

        /// <summary>
        /// Identifies the <see cref="Bands"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty BandsProperty = RegisterDependencyProperty(typeof(BandCloudView), "Bands", typeof(int));

        /// <summary>
        /// Identifies the <see cref="SortBindings"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty SortBindingsProperty = RegisterDependencyProperty(typeof(BandCloudView), "SortBindings", typeof(SortBindings));

        /// <summary>
        /// Identifies the <see cref="VerticalAxisTopLabel"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty VerticalAxisTopLabelProperty = RegisterDependencyProperty(typeof(BandCloudView), "VerticalAxisTopLabel", typeof(string));

        /// <summary>
        /// Identifies the <see cref="VerticalAxisBottomLabel"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty VerticalAxisBottomLabelProperty = RegisterDependencyProperty(typeof(BandCloudView), "VerticalAxisBottomLabel", typeof(string));

        private ScrollViewer        viewer;
        private RepeatButton        topAxisLabel;
        private RepeatButton        bottomAxisLabel;

        private BandCloudViewItems  _Items = new BandCloudViewItems();
        private BandCloudPanel      bandPanel;
        private Timer               animTimer;
        private int                 animIndex = 0;

        /// <summary>
        /// Provides a overriden handler for dependency property changes.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnDependencyPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.Property == UseItemAnimationProperty)
            {
                this.UseItemAnimation = (bool)e.NewValue;
            }
            else if (e.Property == HorizontalSortFieldProperty)
            {
                this.HorizontalSortField = (e.NewValue != null ? e.NewValue.ToString() : String.Empty);
            }
            else if (e.Property == VerticalSortFieldProperty)
            {
                this.VerticalSortField = (e.NewValue != null ? e.NewValue.ToString() : String.Empty);
            }
            else if (e.Property == HorizontalSortDirectionProperty)
            {
                this.HorizontalSortDirection = (SortDirections)e.NewValue;
            }
            else if (e.Property == VerticalSortDirectionProperty)
            {
                this.VerticalSortDirection = (SortDirections)e.NewValue;
            }
            else if (e.Property == VerticalAxisTopLabelProperty)
            {
                this.VerticalAxisTopLabel = (e.NewValue != null ? e.NewValue.ToString() : String.Empty);
            }
            else if (e.Property == VerticalAxisBottomLabelProperty)
            {
                this.VerticalAxisBottomLabel = (e.NewValue != null ? e.NewValue.ToString() : String.Empty);
            }
            else if (e.Property == BandsProperty)
            {
                this.Bands = (int)e.NewValue;
            }
            else if (e.Property == SortBindingsProperty)
            {
                this.SortBindings = (SortBindings)e.NewValue;
            }
            else
            {
                base.OnDependencyPropertyChanged(e);
            }
        }

        /// <summary>
        /// Gets/Sets a value indicating whether the control will animate items when they are created during a data binding operation.
        /// </summary>
        public bool UseItemAnimation
        {
            get { return (bool)GetValue(UseItemAnimationProperty); }
            set { SetValue(UseItemAnimationProperty, value); }
        }

        /// <summary>
        /// Gets/Sets a value which specifies the number of virtual grouping bands to be used on the vertical axis layout pass.
        /// </summary>
        public int Bands
        {
            get { return (int)GetValue(BandsProperty); }
            set { SetValue(BandsProperty, Math.Max(5, value)); }
        }

        /// <summary>
        /// Gets/Sets a collection of <see cref="SortBindings"/> which describe the sorting potentials for the horizontal axis.
        /// </summary>
        public SortBindings SortBindings
        {
            get { return (SortBindings)GetValue(SortBindingsProperty); }
            set { SetValue(SortBindingsProperty, value); }
        }

        /// <summary>
        /// Gets/Sets a string value which names the Name of the property on the data source which determines the horizontal sorting.
        /// </summary>
        public string HorizontalSortField
        {
            get { object val = GetValue(HorizontalSortFieldProperty); return (val == null ? String.Empty : val.ToString()); }
            set { SetValue(HorizontalSortFieldProperty, value); this.InvalidateMeasure(); }
        }

        /// <summary>
        /// Gets/Sets an enumerated value of type <see cref="SortDirections"/> which describes the sort direction of the horizontal axis.
        /// </summary>
        public SortDirections HorizontalSortDirection
        {
            get { return (SortDirections)GetValue(HorizontalSortDirectionProperty); }
            set { SetValue(HorizontalSortDirectionProperty, value); this.InvalidateMeasure(); }
        }

        /// <summary>
        /// Gets/Sets a string value which names the Name of the property on the data source which determines the vertical sorting.
        /// </summary>
        public string VerticalSortField
        {
            get { object val = GetValue(VerticalSortFieldProperty); return (val == null ? String.Empty : val.ToString()); }
            set { SetValue(VerticalSortFieldProperty, value); this.InvalidateMeasure(); }
        }

        /// <summary>
        /// Gets/Sets an enumerated value of type <see cref="SortDirections"/> which describes the sort direction of the vertical axis.
        /// </summary>
        public SortDirections VerticalSortDirection
        {
            get { return (SortDirections)GetValue(VerticalSortDirectionProperty); }
            set { SetValue(VerticalSortDirectionProperty, value); this.InvalidateMeasure(); }
        }

        /// <summary>
        /// Gets/Sets an a string which determines the tooltip text for the vertical axis maximum.
        /// </summary>
        public string VerticalAxisTopLabel
        {
            get { object val = GetValue(VerticalAxisTopLabelProperty); return (val != null ? val.ToString() : String.Empty); }
            set { SetValue(VerticalAxisTopLabelProperty, value); UpdateVerticalAxisTips(); }
        }

        /// <summary>
        /// Gets/Sets an a string which determines the tooltip text for the vertical axis minimum.
        /// </summary>
        public string VerticalAxisBottomLabel
        {
            get { object val = GetValue(VerticalAxisBottomLabelProperty); return (val != null ? val.ToString() : String.Empty); }
            set { SetValue(VerticalAxisBottomLabelProperty, value); UpdateVerticalAxisTips(); }
        }

        /// <summary>
        /// Gets/Sets a <see cref="Size"/> structure which represents the total size of the viewable area including padding for the scrollbars.
        /// </summary>
        public Size ViewportSize
        {
            get
            {
                if (viewer != null)
                {
                    return new Size(Math.Max(0, viewer.ActualWidth - 48), Math.Max(0, viewer.ActualHeight - 48));
                }

                return Size.Empty;
            }
        }

        /// <summary>
        /// Examines an item to determine if it is the appropriate item container for this control
        /// </summary>
        /// <param name="item">The item to examine</param>
        /// <returns>A boolean value indicating that the item is the correct container (true) or not (false).</returns>
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return (item is BandCloudViewItem);
        }

        /// <summary>
        /// Creates a continer item for this control
        /// </summary>
        /// <returns>A <see cref="System.Windows.DependencyObject"/> instance</returns>
        protected override DependencyObject GetContainerForItemOverride()
        {
            BandCloudViewItem item = new BandCloudViewItem();
            item.Opacity = (this.UseItemAnimation ? 0 : 1);
            return item;
        }

        /// <summary>
        /// Performs removal record-keeping for an item container used by this control
        /// </summary>
        /// <param name="element">The visual element being removed</param>
        /// <param name="item">The data item associated with the visual element</param>
        protected override void ClearContainerForItemOverride(DependencyObject element, object item)
        {
            base.ClearContainerForItemOverride(element, item);

            if (element is BandCloudViewItem)
            {
                BandCloudViewItem viewItem = (BandCloudViewItem)element;
                if (_Items.Contains(viewItem)) _Items.Remove(viewItem);
            }
        }

        /// <summary>
        /// Prepares an item element for insertion into this control
        /// </summary>
        /// <param name="element">The visual element to being inserted</param>
        /// <param name="item">The data item associated with the visual element</param>
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);

            if (element is BandCloudViewItem)
            {
                BandCloudViewItem viewItem = (BandCloudViewItem)element;
                if (!_Items.Contains(viewItem)) _Items.Add(viewItem);

                viewItem.Opacity = (this.UseItemAnimation ? 0 : 1);
            }
        }

        internal BandCloudViewItems ViewItems
        {
            get { return _Items; }
        }

        internal void AnimateItems()
        {
            animIndex = 0;
            animTimer.Change(0, 50);
        }

        private void OnAnimateItemLoop(object context)
        {
            if (!DesignerProperties.IsInDesignTool)
            {
                Dispatcher.BeginInvoke(new Action(AdvanceAnimation));
            }
        }

        private void AdvanceAnimation()
        {
            if (animIndex >= 0 && animIndex < _Items.Count)
            {
                _Items[animIndex].StartAnimation();
                animIndex++;
            }
            else
            {
                animTimer.Change(Timeout.Infinite, Timeout.Infinite);
            }
        }

        /// <summary>
        /// (Override) Processes the control template for this <see cref="BandCloudView"/> instance.
        /// </summary>
        public override void OnApplyTemplate()
        {
            viewer = VerifyTemplateChild<ScrollViewer>("ScrollViewer");

            //Set Vertical Axis Labels
            topAxisLabel = GetTemplateChild("TopAxisLabel") as RepeatButton;
            bottomAxisLabel = GetTemplateChild("BottomAxisLabel") as RepeatButton;

            UpdateVerticalAxisTips(); 
            base.OnApplyTemplate();
        }

        private void UpdateVerticalAxisTips()
        {
            if (topAxisLabel != null) ToolTipService.SetToolTip(topAxisLabel, this.VerticalAxisTopLabel);
            if (bottomAxisLabel != null) ToolTipService.SetToolTip(bottomAxisLabel, this.VerticalAxisBottomLabel);
        }

        /// <summary>
        /// (Override) Performs custom steps during an Arrange pass.
        /// </summary>
        /// <param name="finalSize">The size of the control</param>
        /// <returns>The arranged size of the control</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            VerifyPanel();
            return base.ArrangeOverride(finalSize);
        }

        private void VerifyPanel()
        {
            if (bandPanel == null)
            {
                if (viewer != null && viewer.Content != null)
                {
                    ItemsPresenter presenter = (ItemsPresenter)viewer.Content;
                    int count = VisualTreeHelper.GetChildrenCount(presenter);
                    for (int index = 0; index < count; index++)
                    {
                        DependencyObject panelObj = VisualTreeHelper.GetChild(presenter, index);
                        if (panelObj is BandCloudPanel)
                        {
                            //Get a reference to the Cloud Panel for interaction relative to
                            //magnification and arranger selection
                            bandPanel = (BandCloudPanel)panelObj;
                            bandPanel.ParentView = this;
                            break;
                        }
                    }
                }
            }
        }
    }

    /// <summary>
    /// Implements a custom tile-like item class for the <see cref="BandCloudView"/> view control.
    /// </summary>
    public class BandCloudViewItem : System.Windows.Controls.ItemControl
    {
        /// <summary>
        /// Instantiates a <see cref="BandCloudViewItem"/> instance.
        /// </summary>
        public BandCloudViewItem()
        {
            this.DefaultStyleKey = typeof(BandCloudViewItem);

            translate = new TranslateTransform();
            this.RenderTransform = translate;

            //Center Reveals
            revealStory = Animation.CreateStoryboard(TimeSpan.FromSeconds(0.25));
            moveX = Animation.CreateAnimation(revealStory, translate, "X", 0, 0);
            revealStory.Children.Add(moveX);
            moveY = Animation.CreateAnimation(revealStory, translate, "Y", 0, 0);
            revealStory.Children.Add(moveY);
            fadeAnim = Animation.CreateAnimation(revealStory, this, "Opacity", 0, 0);
            revealStory.Children.Add(fadeAnim);
            this.Resources.Add(revealStory);
        }

        private Storyboard          revealStory;
        private DoubleAnimation     moveX;
        private DoubleAnimation     moveY;
        private DoubleAnimation     fadeAnim;
        private TranslateTransform  translate;

        /// <summary>
        /// (Override) Processes the control template for this <see cref="BandCloudViewItem"/> instance.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
        }

        internal void SetAnimationOrigin(double x, double y, double opacity)
        {
            moveX.From = x;
            moveX.To = 0;
            moveY.From = y;
            moveY.To = 0;
            fadeAnim.From = opacity;
            fadeAnim.To = 1;
        }

        internal void StartAnimation()
        {
            revealStory.Begin();
        }
    }

    /// <summary>
    /// A collection of <see cref="BandCloudViewItem"/> item instances.
    /// </summary>
    public class BandCloudViewItems : List<BandCloudViewItem>
    {

    }

    /// <summary>
    /// A custom <see cref="Panel"/> which performs the custom layout and measuring for a <see cref="BandCloudView"/>.
    /// </summary>
    public class BandCloudPanel : System.Windows.Controls.Panel
    {
        /// <summary>
        /// Instantiates a <see cref="BandCloudPanel"/> instance.
        /// </summary>
        public BandCloudPanel()
        {
            this.TileWidth = this.TileHeight = 96;
            this.TileSpacing = 8;
        }

        private BandCloudView    _ParentView;
        private BandCloudSorter  _VerticalSorter;
        private BandCloudSorter  _HorizontalSorter;

        /// <summary>
        /// Gets a reference to the parent <see cref="BandCloudView"/> for the panel instance.
        /// </summary>
        public BandCloudView ParentView
        {
            get 
            { 
                if (_ParentView == null) throw new InvalidOperationException(SystemResources.ERR_BAND_CLOUD_PARENT);
                return _ParentView; 
            }

            internal set 
            { 
                _ParentView = value; 
                if (_ParentView != null) this.InvalidateMeasure();
            }
        }

        /// <summary>
        /// Identifies the <see cref="TileWidth"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty TileWidthProperty = DependencyProperty.Register("TileWidth", typeof(double), typeof(BandCloudPanel), new PropertyMetadata(OnTileWidthPropertyChanged));

        /// <summary>
        /// Identifies the <see cref="TileHeight"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty TileHeightProperty = DependencyProperty.Register("TileHeight", typeof(double), typeof(BandCloudPanel), new PropertyMetadata(OnTileHeightPropertyChanged));

        /// <summary>
        /// Identifies the <see cref="TileSpacing"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty TileSpacingProperty = DependencyProperty.Register("TileSpacing", typeof(double), typeof(BandCloudPanel), new PropertyMetadata(OnTileSpacingPropertyChanged));

        private static void OnTileWidthPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (obj is BandCloudPanel) ((BandCloudPanel)obj).TileWidth = (double)e.NewValue;
        }

        private static void OnTileHeightPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (obj is BandCloudPanel) ((BandCloudPanel)obj).TileHeight = (double)e.NewValue;
        }

        private static void OnTileSpacingPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (obj is BandCloudPanel) ((BandCloudPanel)obj).TileSpacing = (double)e.NewValue;
        }

        /// <summary>
        /// Gets/Sets a double value which determines the width of the band tiles.
        /// </summary>
        public double TileWidth
        {
            get { return (double)GetValue(TileWidthProperty); }
            set { SetValue(TileWidthProperty, value); }
        }

        /// <summary>
        /// Gets/Sets a double value which determines the height of the band tiles.
        /// </summary>
        public double TileHeight
        {
            get { return (double)GetValue(TileHeightProperty); }
            set { SetValue(TileHeightProperty, value); }
        }

        /// <summary>
        /// Gets/Sets a double value which determines the absolute spacing between the band tiles.
        /// </summary>
        public double TileSpacing
        {
            get { return (double)GetValue(TileSpacingProperty); }
            set { SetValue(TileSpacingProperty, value); }
        }

        /// <summary>
        /// Gets/Sets an instance of a <see cref="BandCloudSorter"/> which performs the vertical sorting for the <see cref="BandCloudView"/>.
        /// </summary>
        private BandCloudSorter VerticalSorter
        {
            get
            {
                if (_VerticalSorter == null) _VerticalSorter = new BandCloudSorter(this.ParentView.VerticalSortField, this.ParentView.VerticalSortDirection);
                _VerticalSorter.SortField = this.ParentView.VerticalSortField;
                _VerticalSorter.SortDirection = this.ParentView.VerticalSortDirection;

                return _VerticalSorter;
            }
        }

        /// <summary>
        /// Gets/Sets an instance of a <see cref="BandCloudSorter"/> which performs the horizontal sorting for the <see cref="BandCloudView"/>.
        /// </summary>
        private BandCloudSorter HorizontalSorter
        {
            get
            {
                if (_HorizontalSorter == null) _HorizontalSorter = new BandCloudSorter(this.ParentView.HorizontalSortField, this.ParentView.HorizontalSortDirection);
                _HorizontalSorter.SortField = this.ParentView.HorizontalSortField;
                _HorizontalSorter.SortDirection = this.ParentView.HorizontalSortDirection;

                return _HorizontalSorter;
            }
        }

        private BandCloudBands CalculateBands(BandCloudViewItems items)
        {
            BandCloudBands bands = new BandCloudBands();

            Size size = new Size();
            size.Height = ((this.TileHeight + this.TileSpacing) * this.ParentView.Bands);

            //Sort first by Vertical Access - this is the basis of the Height dimension
            DoubleRange vRange = DoubleRange.Empty;
            items.Sort(this.VerticalSorter);
            if (items.Count > 0)
            {
                vRange.Minimum = ArrangeSorter.GetDoubleSortValue(this.ParentView.VerticalSortField, items[0].DataContext);
                vRange.Maximum = ArrangeSorter.GetDoubleSortValue(this.ParentView.VerticalSortField, items[items.Count - 1].DataContext);
            }

            for (int bandIndex = 0; bandIndex < this.ParentView.Bands; bandIndex++)
            {
                BandCloudBand band = new BandCloudBand();
                band.Y = bandIndex * this.TileHeight;
                bands.Add(band);
            }

            //Set the Y values by vertical bands
            for(int index = 0; index < items.Count; index++)
            {
                BandCloudViewItem item = items[index];
                double yVal = ArrangeSorter.GetDoubleSortValue(this.ParentView.VerticalSortField, item.DataContext);

                int bandIndex = (yVal > 0 ? (int)Math.Round(vRange.AbsoluteRange / yVal) : bands.Count - 1);
                if (bandIndex >= 0 && bandIndex < bands.Count)
                {
                    bands[bandIndex].Items.Add(item);
                }
                else if (bandIndex >= bands.Count)
                {
                    bands[bands.Count - 1].Items.Add(item);
                }
            }

            return bands;
        }

        /// <summary>
        /// (Override) Provides custom layout mesaurement for child <see cref="BandCloudViewItem"/> instances.
        /// </summary>
        /// <param name="availableSize">The available size for the layout</param>
        /// <returns>The required size for the layout</returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            Size size = new Size();

            if (_ParentView != null)
            {
                double width = this.TileHeight;
                double height = this.TileHeight;
                double spacing = this.TileSpacing;

                BandCloudViewItems items = this.ParentView.ViewItems;
                BandCloudBands bands = CalculateBands(items);

                for (int bandIndex = 0; bandIndex < bands.Count; bandIndex++)
                {
                    BandCloudBand band = bands[bandIndex];
                    double right = (band.Items.Count * (this.TileWidth + this.TileSpacing));
                    if (right > size.Width) size.Width = right;
                    if (band.Items.Count > 0) size.Height += (this.TileHeight + this.TileSpacing);
                }
            }

            return size;
        }

        /// <summary>
        /// (Override) Provides custom layout for child <see cref="BandCloudViewItem"/> instances.
        /// </summary>
        /// <param name="finalSize">The final size for the layout</param>
        /// <returns>The actual size for the layout</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            Size size = new Size();

            if (_ParentView != null)
            {
                double width = this.TileHeight;
                double height = this.TileHeight;
                double spacing = this.TileSpacing;

                BandCloudViewItems items = this.ParentView.ViewItems;
                BandCloudBands bands = CalculateBands(items);

                double y = 0;
                Point ptCenter = new Point(this.DesiredSize.Width / 2, this.DesiredSize.Height / 2);

                for (int bandIndex = 0; bandIndex < bands.Count; bandIndex++)
                {
                    BandCloudBand band = bands[bandIndex];

                    if (band.Items.Count > 0)
                    {
                        //Sort Items
                        band.Items.Sort(this.HorizontalSorter);

                        for (int index = 0; index < band.Items.Count; index++)
                        {
                            BandCloudViewItem item = band.Items[index];

                            double x = (index * (this.TileWidth + this.TileSpacing));
                            item.Arrange(new Rect(x, y, this.TileWidth, this.TileHeight));
                            item.SetAnimationOrigin(ptCenter.X - x, ptCenter.Y - y, 0);

                            if (item.Bounds.Right > size.Width) size.Width = item.Bounds.Right;
                        }

                        y += (this.TileHeight + this.TileSpacing);
                        size.Height += (this.TileHeight + this.TileSpacing);
                    }
                }

                if (this.ParentView.UseItemAnimation) this.ParentView.AnimateItems();
            }

            return size;
        }
    }

    /// <summary>
    /// Implements a sorting functor which does comparison based sorting for a <see cref="BandCloudView"/> instance.
    /// </summary>
    public class BandCloudSorter : ArrangeSorter, IComparer<BandCloudViewItem>
    {
        /// <summary>
        /// Instantiates a <see cref="BandCloudSorter"/> instance.
        /// </summary>
        /// <param name="field">The field or property name to sort by</param>
        /// <param name="direction">The direction of the sorting</param>
        public BandCloudSorter(string field, SortDirections direction) : base(field, direction)
        {
        }

        #region IComparer<BandCloudViewItem> Members

        /// <summary>
        /// Performs a comparison of two <see cref="BandCloudViewItem"/> instances based on a property or field name and a sort direction.
        /// </summary>
        /// <param name="x">The first <see cref="BandCloudViewItem"/></param>
        /// <param name="y">The second <see cref="BandCloudViewItem"/></param>
        /// <returns></returns>
        public int Compare(BandCloudViewItem x, BandCloudViewItem y)
        {
            if (String.IsNullOrEmpty(this.SortField)) throw new InvalidOperationException(SystemResources.ERR_BANDSORTER_NO_FIELD);

            object dataX = x.DataContext;
            object dataY = y.DataContext;

            if (dataX != null && dataY != null)
            {
                object valX = GetSortValue(this.SortField, dataX);
                object valY = GetSortValue(this.SortField, dataY);

                if (valX != null && valX is IComparable && valY != null && valY is IComparable)
                {
                    if (this.SortDirection == SortDirections.Ascending)
                        return ((IComparable)valX).CompareTo(valY);
                    else
                        return ((IComparable)valY).CompareTo(valX);
                }
            }

            return 0;
        }

        #endregion
    }

    internal class BandCloudBand
    {
        public BandCloudBand()
        {
            this.Y = 0;
            this.Items = new BandCloudViewItems();
        }

        public double Y { get; set; }
        public BandCloudViewItems Items { get; set; }
    }

    internal class BandCloudBands : List<BandCloudBand>
    {
    }
}
